#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <peekpoke.h>
#include <cbm.h>
#include <stdbool.h>
#include <conio.h>

#include "Dispatcher.h"
#include "Drives.h"
#include "Buffers.h"
#include "Loader.h"
#include "Trim.h"
#include "CopyFiles.h"
#include "Commandlet.h"
#include "Splitter.h"

#pragma code-name("DPCODE");
#pragma rodata-name("DPCODE");

drive_t *drive;

char loadAndExecuteCommandlet(unsigned char driveIndex, char *filename)
{
	unsigned char r = 0;
	char tryLoad = 0; 

	memset(buffer3, 0, sizeof(buffer3));
	strcpy(buffer3, buffer1);

	if(eapi_load(filename) == 1)
	{
		if((r = initCommandlet(driveIndex)) != 0)
		{
			printf("\nError Code: %02X", r);
		}
	}
	else
	{
		tryLoad = 1;
	}

	return tryLoad;
}

void scrolldown(void)
{
	memcpy((void *)0x400, (void *)0x428, 0x3e8);
	memset(0x400 + 24 * 40, ' ', 40);
}

//unsigned char executeScript(unsigned char driveIndex)
//{
//	int r = 0, i = 0;
//	unsigned char byte;
//	tokenCollection_t *collection = NULL;
//	parsedPath_t *path = NULL;
//	char *command = (char *)malloc(256);
//	
//	memset(command, 0, sizeof(command));
//	strcpy(command, buffer1);
//
//
//	collection = splitString(command);
//
//	path = parsePath(1, collection, driveIndex);
//
//	if(getDrive(path->device) == 0xFF)
//	{
//		printf("\nInvalid device.");
//		return 0xFF;
//	}
//	else
//	{
//		drive = &drives[getDrive(path->device)];
//	}
//
//	if(path->file[0] != 0 && !isPattern(path->file))
//	{
//
//		if(drive->type != CBM15x1)
//		{
//			memset(buffer3, 0, sizeof(buffer3));
//			sprintf(buffer3, drive->type == VICE ? "cd:%s" : "cd%s", path->path);
//			executeCommand(drive, buffer3, 0);
//		}
//
//		memset(buffer3, 0, sizeof(buffer3));
//		sprintf(buffer3, "%s,,r", path->file);
//	
//		r = cbm_open(7, drive->device, 2, buffer3);
//		if(r == 0)
//		{
//			while((r = cbm_read(7, buffer3 + i, 1)) == 1 && 
//				!(kbhit() && cgetc() == CH_STOP))
//			{
//				byte = buffer3[i];
//
//				if(byte == '\n')
//				{
//					buffer3[i] = '\0';
//					printf("\n%s", buffer3);
//					if(processCommand(buffer3) > 0)
//					{
//						printf("\nAborting.");
//						break;
//					}
//					memset(buffer3, 0, sizeof(buffer3));
//					i = 0;
//				}
//				else
//					++i;
//			}
//			printf("\nFinsihed [%u]", r);
//		}
//		else
//		{
//			printf("\nNot found.");
//		}
//	}
//
//	cbm_close(7);
//	free(path);
//	free(collection);
//	free(command);
//	return r;
//}

char setBuffer(int startx, int starty, char i, char *buffer, char *newText)
{
	memset(buffer, ' ', i + 1);
	cputsxy(startx, starty, buffer);
	memset(buffer, 0, i + 1);
	strcpy(buffer, newText);
	return strlen(buffer);
}

unsigned char getInput(char *buffer, char cursorChar)
{
	char c = 0, i = 0, j = 0;
	unsigned char result = 0;
	int x = wherex(), y = wherey();
	int startx = x, starty = y;
	memset(buffer, 0, 256);

	while(result == 0)
	{
		//cputcxy(x, y, cursor);
		cursor(1);
		POKE(0x028A, 0x80);
		c = cgetc();
		while(PEEK(207));
		POKE(204,1);
		switch(c)
		{
		case 0x04:
			result = 0x04;
			cputcxy(x, y, ' ');
			break;
		case CH_CURS_UP:
			if(strcmp(buffer, lastcmd) != 0)
			{
				i = setBuffer(startx, starty, i, buffer, lastcmd);
				i = strlen(buffer);
				x = startx + i;
				y = starty;
				while(x > 40)
				{
					x -= 40;
					++y;
					if(y == 25)
					{
						scrolldown();
						--y;
						--starty;
					}
				}
				cputsxy(startx, starty, buffer);
			}
			break;
		case CH_ENTER:
			result = CH_ENTER;
			break;
		case CH_F1:
			result = CH_ENTER;
		case CH_F2:
			i = setBuffer(startx, starty, i, buffer, "dir ");
			cputsxy(startx, starty, buffer);
			x = wherex();
			y = wherey();
			break;
		case CH_F3:
			i = setBuffer(startx, starty, i, buffer, "exec :");
			cputsxy(startx, starty, buffer);
			x = wherex();
			y = wherey();
			break;
		case CH_F4:
			i = setBuffer(startx, starty, i, buffer, "show :");
			cputsxy(startx, starty, buffer);
			x = wherex();
			y = wherey();
			break;
		case CH_F5:
			i = setBuffer(startx, starty, i, buffer, "copy ");
			cputsxy(startx, starty, buffer);
			x = wherex();
			y = wherey();
			break;
		case CH_F6:
			i = setBuffer(startx, starty, i, buffer, "move ");
			cputsxy(startx, starty, buffer);
			x = wherex();
			y = wherey();
			break;
		//case CH_F7:
		//	setBuffer(startx, starty, i, buffer, "cls");
		//	cputsxy(startx, starty, buffer);
		//	x = wherex();
		//	y = wherey();
		//	result = CH_ENTER;
		//	break;
		//case CH_F8:
		//	setBuffer(startx, starty, i, buffer, "exit ");
		//	cputsxy(startx, starty, buffer);
		//	x = wherex();
		//	y = wherey();
		//	break;
		case CH_CURS_DOWN:
			i = setBuffer(startx, starty, i, buffer, "");
			x = startx;
			y = starty;
			gotoxy(x, y);
			i = 0;
			break;
		case CH_CURS_RIGHT:
			break;
		case CH_DEL:
		case CH_CURS_LEFT:
			if(i > 0)
			{
				buffer[i--] = 0;
				--x;
				if(x < 0)
				{
					--y;
					x = 39;
				}
				cputcxy(x, y, ' ');
				gotoxy(x, y);
			}
			break;
		default:
			if(i < 255)
			{
				buffer[i++] = c;			
				cputcxy(x, y, c);
				++x;
				if(x == 40)
				{
					if(y < 24)
					{
						++y;
						x = 0;
					}
					else
					{
						scrolldown();
						y = 24;
						x = 0;
						--starty;
					}
				}
			}
			break;
		}
	}

	return result;
}

unsigned char processCommand(char *command)
{
	unsigned char driveIndex = getDrive(drive->device), c = 0;
	bool tryLoad = false;
	char color = 0, r = 0;
	int iTemp, iTemp2;

	tryLoad = false;
	memset(buffer1, 0, sizeof(buffer1));

	if(command == NULL)
	{
		printf("\n%02u#%u:%s> ", drive->device, drive->drive, drive->path);
		c = getInput(buffer1, 166);
	}
	else
	{
		strcpy(buffer1, command);
		c = CH_ENTER;
	}

	if(c == CH_ENTER)
	{
		strcpy(lastcmd, buffer1);
		memset(arguments, 0, sizeof(arguments));

		if(strcmp(buffer1, "exit") == 0) 
		{
			POKE(1, 0x37);
			POKE(0xDE02, 4);
			asm("jmp ($fffc)");
		}
		
		if(sscanf(buffer1, "#%d", &iTemp) == 1)
		{
			iTemp2 = drive->device;

			driveIndex = getDrive((unsigned char)iTemp);

			if(driveIndex == 0xFF) 
			{
				printf("\nUnknown drive.");
				driveIndex = getDrive((unsigned char)iTemp2);
			}

			drive = &drives[driveIndex];
		}

		else if(sscanf(buffer1, ":%d", &iTemp) == 1)
		{
			drive->drive = (unsigned char)iTemp;
		}

		else if(sscanf(buffer1, "cd %s", arguments) == 1 ||
			strcmp(rtrim(buffer1, ' '), "cd") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.CD");
		}

		else if(sscanf(buffer1, "dir %s", arguments) == 1 ||
			strcmp(buffer1, "dir") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.DIR");
		}

		else if(sscanf(buffer1, "mkdir %s", arguments) == 1 ||
			strcmp(buffer1, "mkdir") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.MKDIR");
		}

		else if(sscanf(buffer1, "del %s", arguments) == 1 ||
			strcmp(buffer1, "del") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.DEL");
		}

		else if(sscanf(buffer1, "rmdir %s", arguments) == 1 ||
			strcmp(buffer1, "rmdir") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.RMDIR");
		}

		else if(sscanf(buffer1, "ren %s", buffer2) > 0 ||
			strcmp(buffer1, "ren") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.REN");
		}

		else if(sscanf(buffer1, "copy %s", buffer2) > 0 ||
			sscanf(buffer1, "move %s", buffer2) > 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.COPYMOVE");
		}

		else if(sscanf(buffer1, "help %s", buffer2) > 0 ||
			strcmp(buffer1, "help") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.HELP");
		}

		else if(sscanf(buffer1, "show %s", buffer2) > 0 ||
			strcmp(buffer1, "show") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.SHOW");
		}

		else if(sscanf(buffer1, "mkimg %s", buffer2) > 0 ||
			strcmp(buffer1, "mkimg") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.MKIMG");
		}

		else if(sscanf(buffer1, "wrimg %s", buffer2) > 0 ||
			strcmp(buffer1, "wrimg") == 0)
		{
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.WRIMG");
		}

		else if (strcmp(buffer1, "cls") == 0)
		{
			putchar(147);
		}

		else if (strcmp(buffer1, "pause") == 0)
		{
			printf("\nHit any key.");
			cgetc();
		}

		else if (sscanf(buffer1, "echo %s", buffer2) > 0)
		{
			printf("\n%s", buffer1 + 5);
		}
		else if(strcmp(buffer1, "echo") == 0)
		{
			printf("\n");
		}

		else if (sscanf(buffer1, "@%s", arguments) == 1)
		{
			executeCommand(drive, arguments, 1);
		}

		else if (strcmp(buffer1, "@") == 0)
		{
			executeCommand(drive, "", 1);
		}

		else if(sscanf(buffer1, "exec %s", buffer2) == 1)
		{
			scriptposition = 0;
			memset(scriptlast, 0, sizeof(scriptlast));
			tryLoad = loadAndExecuteCommandlet(driveIndex, "CMD.EXEC");
		}

		//else { tryLoad = true; }
		else if (buffer1[0] != 0)
		{
			printf("\nUnknown command.");
		}
	}
	else if(c == 0x04)
	{
		++driveIndex;
		if(driveIndex == 6) driveIndex = 0;
		if(drives[driveIndex].device == 0) driveIndex = 0;
		drive = &drives[driveIndex];
	}
	//else
	//{
	//	printf("\nUnknown result. [%u]", c);
	//}
	//if(tryLoad)
	//{
	//	if(loadProgram(drive, buffer1) == 0)
	//	{
	//		
	//	}
	//}

	return 0;
}

void processCommands(void)
{
	unsigned char driveIndex = 0, color = 0;

	drive = &(drives[driveIndex]);

	if(drive->device == 0)
	{
		while(1) POKE(0xd020, ++color);
	}

	while(processCommand(NULL) == 0);
}

//char loadProgram(drive_t *drive, char *command)
//{
//	unsigned char r = 0;
//	unsigned char la[2];
//	
//	sscanf(command, "%s %s", buffer1, arguments);
//	sprintf(buffer3, "%s,p,r", buffer1);
//	r = cbm_open(15,drive->device,15, "");
//	r = cbm_open(1,drive->device,0,buffer3);
//	cbm_read(15, buffer2, sizeof(buffer2));
//	if(buffer2[0] == '0' && buffer2[1] == '0')
//	{
//		r = cbm_read(1, la, 2);
//		cbm_close(15);
//		cbm_close(1);
//
//		printf("\nExecuting %s at %02X%02X...", buffer1, la[1], la[0]);
//		execProgram(drive->device, (unsigned int)buffer1 / 256, (unsigned int)buffer1 % 256, (unsigned char)strlen(buffer1), la[0], la[1]);
//		return 1;
//	}
//	else
//	{
//		printf("\nERROR: %s", buffer2);
//	}
//
//	cbm_close(15);
//	cbm_close(1);
//	return 0;
//}

bool executeCommand(drive_t *drive, char *command, unsigned char echo)
{
	bool result = false;
	unsigned char r = 0;
	if((r = cbm_open(15, drive->device, 15, command)) == 0)
	{
		memset(buffer1, 0, sizeof(buffer1));
		cbm_read(15, buffer1, sizeof(buffer1));
		result = (buffer1[0] == '0' && buffer1[1] == '0');
		if(echo == 1) printf("\n%02u#%u: %s", drive->device, drive->drive, buffer1);
	}

	cbm_close(15);
	return result;
}

void about(void)
{
	printf("\nEasyCLI Beta %s %s", __DATE__, __TIME__);
	printf("\nhttp://easycli.codeplex.com");
}